{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "\n",
    "# 固定随机种子，使得运行结果可以稳定复现\n",
    "torch.manual_seed(1024)\n",
    "# 产生训练用的数据\n",
    "x_origin = torch.linspace(100, 300, 200)\n",
    "# 将变量X归一化，否则梯度下降法很容易不稳定\n",
    "x = (x_origin - torch.mean(x_origin)) / torch.std(x_origin)\n",
    "epsilon = torch.randn(x.shape)\n",
    "y = 10 * x + 5 + epsilon"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 为了使用PyTorch的高层封装函数，我们通过继承Module类来定义函数\n",
    "class Linear(torch.nn.Module):\n",
    "    \n",
    "    def __init__(self):\n",
    "        \"\"\"\n",
    "        定义线性回归模型的参数：a, b\n",
    "        \"\"\"\n",
    "        super().__init__()\n",
    "        self.a = torch.nn.Parameter(torch.zeros(()))\n",
    "        self.b = torch.nn.Parameter(torch.zeros(()))\n",
    "\n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        根据当前的参数估计值，得到模型的预测结果\n",
    "        参数\n",
    "        ----\n",
    "        x ：torch.tensor，变量x\n",
    "        返回\n",
    "        ----\n",
    "        y_pred ：torch.tensor，模型预测值\n",
    "        \"\"\"\n",
    "        return self.a * x + self.b\n",
    "\n",
    "    def string(self):\n",
    "        \"\"\"\n",
    "        输出当前模型的结果\n",
    "        \"\"\"\n",
    "        return f'y = {self.a.item():.2f} * x + {self.b.item():.2f}'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Step 1, Loss:  125.25; Result: y = 1.98 * x + 1.02\n",
      "Step 2, Loss:  80.72; Result: y = 3.56 * x + 1.83\n",
      "Step 3, Loss:  52.16; Result: y = 4.83 * x + 2.49\n",
      "Step 4, Loss:  33.84; Result: y = 5.85 * x + 3.01\n",
      "Step 5, Loss:  22.10; Result: y = 6.66 * x + 3.42\n",
      "Step 6, Loss:  14.57; Result: y = 7.31 * x + 3.76\n",
      "Step 7, Loss:  9.74; Result: y = 7.83 * x + 4.03\n",
      "Step 8, Loss:  6.64; Result: y = 8.25 * x + 4.24\n",
      "Step 9, Loss:  4.66; Result: y = 8.59 * x + 4.41\n",
      "Step 10, Loss:  3.38; Result: y = 8.85 * x + 4.55\n",
      "Step 11, Loss:  2.56; Result: y = 9.07 * x + 4.66\n",
      "Step 12, Loss:  2.04; Result: y = 9.24 * x + 4.74\n",
      "Step 13, Loss:  1.71; Result: y = 9.38 * x + 4.81\n",
      "Step 14, Loss:  1.49; Result: y = 9.49 * x + 4.87\n",
      "Step 15, Loss:  1.35; Result: y = 9.58 * x + 4.91\n",
      "Step 16, Loss:  1.26; Result: y = 9.65 * x + 4.95\n",
      "Step 17, Loss:  1.21; Result: y = 9.71 * x + 4.98\n",
      "Step 18, Loss:  1.17; Result: y = 9.75 * x + 5.00\n",
      "Step 19, Loss:  1.15; Result: y = 9.79 * x + 5.02\n",
      "Step 20, Loss:  1.13; Result: y = 9.82 * x + 5.03\n"
     ]
    }
   ],
   "source": [
    "# 定义模型\n",
    "model = Linear()\n",
    "# 确定最优化算法\n",
    "learning_rate = 0.1\n",
    "optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)\n",
    "\n",
    "for t in range(20):\n",
    "    # 根据当前的参数估计值，得到模型的预测结果\n",
    "    # 也就是调用forward函数\n",
    "    y_pred = model(x)\n",
    "    # 计算损失函数\n",
    "    loss = (y - y_pred).pow(2).mean()\n",
    "    # 将上一次的梯度清零\n",
    "    optimizer.zero_grad()\n",
    "    # 计算损失函数的梯度\n",
    "    loss.backward()\n",
    "    # 迭代更新模型参数的估计值\n",
    "    optimizer.step()\n",
    "    print(f'Step {t + 1}, Loss: {loss: .2f}; Result: {model.string()}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Step 1, Loss:  125.25; Result: y = 1.98 * x + 1.02\n",
      "Step 2, Loss:  80.72; Result: y = 3.56 * x + 1.83\n",
      "Step 3, Loss:  52.16; Result: y = 4.83 * x + 2.49\n",
      "Step 4, Loss:  33.84; Result: y = 5.85 * x + 3.01\n",
      "Step 5, Loss:  22.10; Result: y = 6.66 * x + 3.42\n",
      "Step 6, Loss:  14.57; Result: y = 7.31 * x + 3.76\n",
      "Step 7, Loss:  9.74; Result: y = 7.83 * x + 4.03\n",
      "Step 8, Loss:  6.64; Result: y = 8.25 * x + 4.24\n",
      "Step 9, Loss:  4.66; Result: y = 8.59 * x + 4.41\n",
      "Step 10, Loss:  3.38; Result: y = 8.85 * x + 4.55\n",
      "Step 11, Loss:  2.56; Result: y = 9.07 * x + 4.66\n",
      "Step 12, Loss:  2.04; Result: y = 9.24 * x + 4.74\n",
      "Step 13, Loss:  1.71; Result: y = 9.38 * x + 4.81\n",
      "Step 14, Loss:  1.49; Result: y = 9.49 * x + 4.87\n",
      "Step 15, Loss:  1.35; Result: y = 9.58 * x + 4.91\n",
      "Step 16, Loss:  1.26; Result: y = 9.65 * x + 4.95\n",
      "Step 17, Loss:  1.21; Result: y = 9.71 * x + 4.98\n",
      "Step 18, Loss:  1.17; Result: y = 9.75 * x + 5.00\n",
      "Step 19, Loss:  1.15; Result: y = 9.79 * x + 5.02\n",
      "Step 20, Loss:  1.13; Result: y = 9.82 * x + 5.03\n"
     ]
    }
   ],
   "source": [
    "# 利用代码实现PyTorch封装的梯度下降法\n",
    "model = Linear()\n",
    "for t in range(20):\n",
    "    # 根据当前的参数估计值，得到模型的预测结果\n",
    "    # 也就是调用forward函数\n",
    "    y_pred = model(x)\n",
    "    # 计算损失函数\n",
    "    loss = (y - y_pred).pow(2).mean()\n",
    "    # 计算损失函数的梯度\n",
    "    loss.backward()\n",
    "    with torch.no_grad():\n",
    "        for param in model.parameters():\n",
    "            # 迭代更新模型参数的估计值，等同于optimizer.step()\n",
    "            param -= learning_rate * param.grad\n",
    "            # 将梯度清零，等同于optimizer.zero_grad()\n",
    "            param.grad = None\n",
    "    print(f'Step {t + 1}, Loss: {loss: .2f}; Result: {model.string()}')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
